home *** CD-ROM | disk | FTP | other *** search
/ The 640 MEG Shareware Studio 2 / The 640 Meg Shareware Studio CD-ROM Volume II (Data Express)(1993).ISO / clang / dosrcss.zip / CO.C < prev    next >
C/C++ Source or Header  |  1990-07-18  |  27KB  |  742 lines

  1. /* Copyright (C) 1982, 1988, 1989 Walter Tichy
  2.    Distributed under license by the Free Software Foundation, Inc.
  3.  
  4. This file is part of RCS.
  5.  
  6. RCS is free software; you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation; either version 1, or (at your option)
  9. any later version.
  10.  
  11. RCS is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. GNU General Public License for more details.
  15.  
  16. You should have received a copy of the GNU General Public License
  17. along with RCS; see the file COPYING.  If not, write to
  18. the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  19.  
  20. Report problems and direct all questions to:
  21.  
  22.     rcs-bugs@cs.purdue.edu
  23.  
  24. */
  25.  
  26. /*
  27.  *                     RCS checkout operation
  28.  */
  29. #ifndef lint
  30. static char rcsid[]=
  31. "$Header: /site/tmp/dosrcs/src/RCS/co.c,v 5.4 90/07/15 20:23:39 lfk Release $ Purdue CS";
  32. #endif
  33. /*****************************************************************************
  34.  *                       check out revisions from RCS files
  35.  *****************************************************************************
  36.  */
  37.  
  38.  
  39. /* $Log:    co.c,v $
  40.  * Revision 5.4  90/07/15  20:23:39  lfk
  41.  * Most major fixes added between rev 5.1 and rev 5.5:
  42.  *     signals fixed so they work on MS-DOS
  43.  *     Added MKS arguments code so argv can be large
  44.  *     added code to handle slashes a'la Unix
  45.  *     added more file extensions to system from MS-DOS
  46.  * 
  47.  * Revision 5.3  90/07/15  15:11:31  ROOT_DOS
  48.  * 
  49.  * Test check in for ^C problem
  50.  * 
  51.  * Revision 5.2  90/07/15  11:30:48  ROOT_DOS
  52.  * DOS version of RCS 4.0 checked in for MODS
  53.  * by lfk@athena.mit.edu
  54.  * Also update to MSC 6.0
  55.  * 
  56.  * revision 5.1 koya 90/01/25 01:11:44
  57.  * Initial revision
  58.  * 
  59.  * Revision 4.7  89/05/01  15:11:41  narten
  60.  * changed copyright header to reflect current distribution rules
  61.  * 
  62.  * Revision 4.6  88/11/08  12:02:31  narten
  63.  * changes from  eggert@sm.unisys.com (Paul Eggert)
  64.  * 
  65.  * Revision 4.6  88/08/09  19:12:15  eggert
  66.  * Fix "co -d" core dump; rawdate wasn't always initialized.
  67.  * Use execv(), not system(); fix putchar('\0') and diagnose() botches; remove lint
  68.  * 
  69.  * Revision 4.5  87/12/18  11:35:40  narten
  70.  * lint cleanups (from Guy Harris)
  71.  * 
  72.  * Revision 4.4  87/10/18  10:20:53  narten
  73.  * Updating version numbers changes relative to 1.1, are actually
  74.  * relative to 4.2
  75.  * 
  76.  * Revision 1.3  87/09/24  13:58:30  narten
  77.  * Sources now pass through lint (if you ignore printf/sprintf/fprintf 
  78.  * warnings)
  79.  * 
  80.  * Revision 1.2  87/03/27  14:21:38  jenkins
  81.  * Port to suns
  82.  * 
  83.  * Revision 1.1  84/01/23  14:49:58  kcs
  84.  * Initial revision
  85.  * 
  86.  * Revision 4.2  83/12/05  13:39:48  wft
  87.  * made rewriteflag external.
  88.  * 
  89.  * Revision 4.1  83/05/10  16:52:55  wft
  90.  * Added option -u and -f.
  91.  * Added handling of default branch.
  92.  * Replaced getpwuid() with getcaller().
  93.  * Removed calls to stat(); now done by pairfilenames().
  94.  * Changed and renamed rmoldfile() to rmworkfile().
  95.  * Replaced catchints() calls with restoreints(), unlink()--link() with rename();
  96.  * 
  97.  * Revision 3.7  83/02/15  15:27:07  wft
  98.  * Added call to fastcopy() to copy remainder of RCS file.
  99.  *
  100.  * Revision 3.6  83/01/15  14:37:50  wft
  101.  * Added ignoring of interrupts while RCS file is renamed; this avoids
  102.  * deletion of RCS files during the unlink/link window.
  103.  *
  104.  * Revision 3.5  82/12/08  21:40:11  wft
  105.  * changed processing of -d to use DATEFORM; removed actual from
  106.  * call to preparejoin; re-fixed printing of done at the end.
  107.  *
  108.  * Revision 3.4  82/12/04  18:40:00  wft
  109.  * Replaced getdelta() with gettree(), SNOOPDIR with SNOOPFILE.
  110.  * Fixed printing of "done".
  111.  *
  112.  * Revision 3.3  82/11/28  22:23:11  wft
  113.  * Replaced getlogin() with getpwuid(), flcose() with ffclose(),
  114.  * %02d with %.2d, mode generation for working file with WORKMODE.
  115.  * Fixed nil printing. Fixed -j combined with -l and -p, and exit
  116.  * for non-existing revisions in preparejoin().
  117.  *
  118.  * Revision 3.2  82/10/18  20:47:21  wft
  119.  * Mode of working file is now maintained even for co -l, but write permission
  120.  * is removed.
  121.  * The working file inherits its mode from the RCS file, plus write permission
  122.  * for the owner. The write permission is not given if locking is strict and
  123.  * co does not lock.
  124.  * An existing working file without write permission is deleted automatically.
  125.  * Otherwise, co asks (empty answer: abort co).
  126.  * Call to getfullRCSname() added, check for write error added, call
  127.  * for getlogin() fixed.
  128.  *
  129.  * Revision 3.1  82/10/13  16:01:30  wft
  130.  * fixed type of variables receiving from getc() (char -> int).
  131.  * removed unused variables.
  132.  */
  133.  
  134.  
  135.  
  136.  
  137. #include "rcsbase.h"
  138. #include "time.h"
  139. #include <sys/types.h>
  140. #include <sys/stat.h>
  141.  
  142. #ifndef lint
  143. static char rcsbaseid[] = RCSBASE;
  144. #endif
  145. #ifdef MSDOS
  146. static char co[] = CO;
  147. static char merge[] = "\genurcs\src\merge.bat";
  148. #else
  149. static char co[] = CO;
  150. static char merge[] = MERGE;
  151. #endif /* MSDOS */
  152.  
  153. extern FILE * fopen();
  154. extern int    rename();
  155. extern char * getcaller();          /*get login of caller                   */
  156. extern struct hshentry * genrevs(); /*generate delta numbers                */
  157. extern char * getancestor();
  158. extern int  nextc;                  /*next input character                  */
  159. extern int  nerror;                 /*counter for errors                    */
  160. extern char Kdesc[];            /*keyword for description            */
  161. extern char * buildrevision();      /*constructs desired revision           */
  162. extern int    buildjoin();          /*join several revisions                */
  163. extern char * mktempfile();         /*temporary file name generator         */
  164. extern struct hshentry * findlock();/*find (and delete) a lock              */
  165. extern struct lock * addlock();     /*add a new lock                        */
  166. extern long   maketime();           /*convert parsed time to unix time.     */
  167. extern struct tm * localtime();     /*convert unixtime into a tm-structure  */
  168. extern FILE * finptr;               /* RCS input file                       */
  169. extern FILE * frewrite;             /* new RCS file                         */
  170. extern int    rewriteflag;          /* indicates whether input should be    */
  171.                     /* echoed to frewrite                   */
  172.  
  173. char * newRCSfilename, * neworkfilename;
  174. char * RCSfilename, * workfilename;
  175. #ifdef MSDOS
  176. char tmpdir[NCPPN];
  177. char *gettmpdir();
  178. #endif /* MSDOS */
  179. extern struct stat RCSstat, workstat; /* file status of RCS and work file   */
  180. extern int  haveRCSstat, haveworkstat;/* status indicators                  */
  181.  
  182. char * date, * rev, * state, * author, * join;
  183. char finaldate[datelength];
  184.  
  185. int forceflag, lockflag, unlockflag, tostdout;
  186. char * caller;                        /* caller's login;                    */
  187. extern quietflag;
  188.  
  189. char numericrev[revlength];           /* holds expanded revision number     */
  190. struct hshentry * gendeltas[hshsize]; /* stores deltas to be generated      */
  191. struct hshentry * targetdelta;        /* final delta to be generated        */
  192.  
  193. char * joinlist[joinlength];          /* pointers to revisions to be joined */
  194. int lastjoin;                         /* index of last element in joinlist  */
  195.  
  196. #ifdef MKS
  197. main(int argc, char *argv[], char *env[])
  198. #else
  199. main (argc, argv)
  200. int argc;
  201. char * argv[];
  202. #endif /* MKS */
  203. {
  204.         int killock;                  /* indicates whether a lock is removed*/
  205.         char * cmdusage;
  206.         struct tm parseddate, *ftm;
  207.         char * rawdate;
  208.         long unixtime;
  209.  
  210. #ifdef MKS
  211.     int z = 0;
  212.     int ARGC = 0;
  213.     char **tmp;
  214.     char *env_name;
  215. #    define MAXARGS 500        /* This means 500 items on the command line */
  216.     if (!(tmp = (char **)malloc(sizeof(char *)*(MAXARGS+1)))) {
  217.         fprintf(stderr, "%s: can't allocate space for arguments\n",argv[0]);
  218.         exit(1);
  219.     }
  220.     for ( z = 0 ; env[z] != NULL; z++) {    /* loop through environment */
  221.         if (*env[z] == '~') {    /* testing for entries begining with '~' */
  222.             *++env[z];            /* increment pointer to delete '~' */
  223.             tmp[ARGC++] = env[z];    /* add it to our new array */
  224.             if (z >= MAXARGS) {
  225.                 fprintf(stderr, "%s: can't handle any more arguments\n", argv[0]);
  226.                 goto list;
  227.             }
  228.         }
  229.         else if (*env[z] == '_') {    /* testing for entries begining with _ */
  230.             *++env[z] ; *++env[z];    /* move past the '_' and the '=' */
  231.             env_name = env[z];    /* copy the name */
  232.         }
  233.     }
  234. list:
  235.     if ( STREQ( (char *) argv[0] , env_name ) ) {    /* test name against startup args */
  236.         /* environment arguments meant for this program */
  237. #    ifdef DEBUG
  238.         printf("Using shell supplied args\n");
  239. #    endif
  240.         argc = ARGC;
  241.         tmp[ARGC] = NULL;    /* the terminal NULL */
  242.         argv = tmp;
  243.     }
  244. #    ifdef DEBUG
  245.     else 
  246.         printf("Using startup supplied args\n");
  247. #    endif /* debug */
  248. #endif /* MKS */
  249.  
  250.         catchints();
  251.            cmdid = "co";
  252.         cmdusage = "command format:\nco -f[rev] -l[rev] -p[rev] -q[rev] -r[rev] -ddate -sstate -w[login] -jjoinlist file ...";
  253.         date = rev = state = author = join = nil;
  254.         forceflag = lockflag = unlockflag = tostdout = quietflag = false;
  255.         caller=getcaller();
  256.         rawdate = "";
  257.  
  258.         while (--argc,++argv, argc>=1 && ((*argv)[0] == '-')) {
  259.                 switch ((*argv)[1]) {
  260.  
  261.                 case 'r':
  262.                 revno:  if ((*argv)[2]!='\0') {
  263.                                 if (rev!=nil) warn("Redefinition of revision number");
  264.                                 rev = (*argv)+2;
  265.                         }
  266.                         break;
  267.  
  268.         case 'f':
  269.             forceflag=true;
  270.             goto revno;
  271.  
  272.                 case 'l':
  273.                         lockflag=true;
  274.                         if (unlockflag) {
  275.                                 warn("-l has precedence over -u");
  276.                                 unlockflag=false;
  277.                         }
  278.                         goto revno;
  279.  
  280.                 case 'u':
  281.                         unlockflag=true;
  282.                         if (lockflag) {
  283.                                 warn("-l has precedence over -u");
  284.                                 unlockflag=false;
  285.                         }
  286.                         goto revno;
  287.  
  288.                 case 'p':
  289.                         tostdout=true;
  290.                         goto revno;
  291.  
  292.                 case 'q':
  293.                         quietflag=true;
  294.                         goto revno;
  295.  
  296.                 case 'd':
  297.                         if ((*argv)[2]!='\0') {
  298.                                 if (date!=nil) warn("Redefinition of -d option");
  299.                                 rawdate=(*argv)+2;
  300.                         }
  301.                         /* process date/time */
  302.                         if (partime(rawdate,&parseddate)==0)
  303.                                 faterror("Can't parse date/time: %s",rawdate);
  304.                         if ((unixtime=maketime(&parseddate))== 0L)
  305.                                 faterror("Inconsistent date/time: %s",rawdate);
  306.                         ftm=localtime(&unixtime);
  307.                         VOID sprintf(finaldate,DATEFORM,
  308.                         ftm->tm_year,ftm->tm_mon+1,ftm->tm_mday,ftm->tm_hour,ftm->tm_min,ftm->tm_sec);
  309.                         date=finaldate;
  310.                         break;
  311.  
  312.                 case 'j':
  313. #ifdef MSDOS /* Current Version can't treat join */
  314.             faterror("Current Version can't treat join");
  315. #else            
  316.                         if ((*argv)[2]!='\0'){
  317.                                 if (join!=nil)warn("Redefinition of -j option");
  318.                                 join = (*argv)+2;
  319.                         }
  320.                         break;
  321. #endif /* MSDOS */
  322.  
  323.                 case 's':
  324.                         if ((*argv)[2]!='\0'){
  325.                                 if (state!=nil)warn("Redefinition of -s option");
  326.                                 state = (*argv)+2;
  327.                         }
  328.                         break;
  329.  
  330.                 case 'w':
  331.                         if (author!=nil)warn("Redefinition of -w option");
  332.                         if ((*argv)[2]!='\0')
  333.                                 author = (*argv)+2;
  334.                         else    author = caller;
  335.                         break;
  336.  
  337.                 default:
  338.                         faterror("unknown option: %s\n%s", *argv,cmdusage);
  339.  
  340.                 };
  341.         } /* end of option processing */
  342.  
  343.         if (argc<1) faterror("No input file\n%s",cmdusage);
  344.  
  345. #ifdef MSDOS
  346.     strcpy( tmpdir, gettmpdir() );
  347. #endif /* MSDOS */
  348.         /* now handle all filenames */
  349.         do {
  350.         rewriteflag=false;
  351.         finptr=frewrite=NULL;
  352.         neworkfilename=nil;
  353.  
  354.         if (!pairfilenames(argc,argv,true,tostdout)) continue;
  355.  
  356.         /* now RCSfilename contains the name of the RCS file, and finptr
  357.          * the file descriptor. If tostdout is false, workfilename contains
  358.          * the name of the working file, otherwise undefined (not nil!).
  359.          * Also, RCSstat, workstat, and haveworkstat have been set.
  360.          */
  361.         diagnose("%s  -->  %s", RCSfilename,tostdout?"stdout":workfilename);
  362.  
  363.  
  364.         if (!tostdout && !trydiraccess(workfilename)) continue; /* give up */
  365.         if ((lockflag||unlockflag) && !checkaccesslist(caller)) continue;     /* give up */
  366.         if (!trysema(RCSfilename,lockflag||unlockflag)) continue;           /* give up */
  367.  
  368.  
  369.         gettree();  /* reads in the delta tree */
  370.  
  371.         if (Head==nil) {
  372.                 /* no revisions; create empty file */
  373.                 diagnose("no revisions present; generating empty revision 0.0");
  374.                 if (!tostdout)
  375.                         if (!creatempty()) continue;
  376.                 /* Can't reserve a delta, so don't call addlock */
  377.         } else {
  378.                 if (rev!=nil) {
  379.                         /* expand symbolic revision number */
  380.                         if (!expandsym(rev,numericrev))
  381.                                 continue;
  382.         } elsif (unlockflag && (targetdelta=findlock(caller,false))!=nil) {
  383.             VOID strcpy(numericrev,targetdelta->num);
  384.                 } elsif (Dbranch!=nil) {
  385.                         VOID strcpy(numericrev,Dbranch->num);
  386.         } else  numericrev[0]='\0'; /* empty */
  387.                 /* get numbers of deltas to be generated */
  388.                 if (!(targetdelta=genrevs(numericrev,date,author,state,gendeltas)))
  389.                         continue;
  390.                 /* check reservations */
  391.                 if (lockflag && !addlock(targetdelta,caller))
  392.                         continue;
  393.  
  394.                 if (unlockflag) {
  395.                         if((killock=rmlock(caller,targetdelta))== -1)
  396.                                 continue;
  397.                 } else {
  398.                         killock=0;
  399.                 }
  400.  
  401.                 if (join && !preparejoin()) continue;
  402.  
  403.         diagnose("revision %s%s",targetdelta->num,
  404.              lockflag?" (locked)":
  405.              unlockflag?" (unlocked)":"");
  406.  
  407.                 /* remove old working file if necessary */
  408.                 if (!tostdout)
  409.                         if (!rmworkfile()) continue;
  410.  
  411.                 /* prepare for rewriting the RCS file */
  412.                 if (lockflag||(killock==1)) {
  413.                         newRCSfilename=mktempfile(RCSfilename,NEWRCSFILE);
  414.                         if ((frewrite=fopen(newRCSfilename, "w"))==NULL) {
  415.                                 error("Can't open file %s",newRCSfilename);
  416.                                 continue;
  417.                         }
  418.                         putadmin(frewrite);
  419.                         puttree(Head,frewrite);
  420.                         VOID fprintf(frewrite, "\n\n%s%c",Kdesc,nextc);
  421.                         rewriteflag=true;
  422.                 }
  423.  
  424.                 /* skip description */
  425.                 getdesc(false); /* don't echo*/
  426.  
  427.                 if (!(neworkfilename=buildrevision(gendeltas,targetdelta,
  428. #ifdef MSDOS
  429.                       tostdout?(join!=nil? tmpdir:(char *)nil):workfilename,true)))
  430. #else
  431.                       tostdout?(join!=nil?"/tmp/":(char *)nil):workfilename,true)))
  432. #endif /* MSDOS */
  433.                                 continue;
  434.  
  435.                 if ((lockflag||killock==1)&&nerror==0) {
  436.                         /* rewrite the rest of the RCSfile */
  437.                         fastcopy(finptr,frewrite);
  438.                         ffclose(frewrite); frewrite=NULL;
  439.             ignoreints();
  440. #ifdef MSDOS
  441.                         if (Rename(newRCSfilename,RCSfilename) != 0 ) {
  442. #else
  443.                         if (rename(newRCSfilename,RCSfilename)<0) {
  444. #endif /* MSDOS */
  445.                                 error("Can't rewrite %s; saved in: %s",
  446.                                 RCSfilename, newRCSfilename);
  447.                                 newRCSfilename[0]='\0'; /* avoid deletion*/
  448.                                 restoreints();
  449.                                 break;
  450.                         }
  451.                         newRCSfilename[0]='\0'; /* avoid re-deletion by cleanup()*/
  452. #ifdef MSDOS
  453.                         if (chmod(RCSfilename,RCSstat.st_mode & ~S_IWRITE)<0)
  454. #else
  455.                         if (chmod(RCSfilename,RCSstat.st_mode & ~0222)<0)
  456. #endif /* MSDOS */
  457.                             warn("Can't preserve mode of %s",RCSfilename);
  458.                         restoreints();
  459.                 }
  460.  
  461. #               ifdef SNOOPFILE
  462.                 logcommand("co",targetdelta,gendeltas,caller);
  463. #               endif
  464.  
  465.                 if (join) {
  466.                         rmsema(); /* kill semaphore file so other co's can proceed */
  467.             if (!buildjoin(neworkfilename)) continue;
  468.                 }
  469.                 if (!tostdout) {
  470. #ifdef MSDOS
  471.             if (Rename(neworkfilename,workfilename) <0) {
  472. #else
  473.             if (rename(neworkfilename,workfilename) <0) {
  474. #endif /* MSDOS */
  475.                                 error("Can't create %s; see %s",workfilename,neworkfilename);
  476.                                 neworkfilename[0]= '\0'; /*avoid deletion*/
  477.                                 continue;
  478.                         }
  479.             neworkfilename[0]= '\0'; /*avoid re-deletion by cleanup()*/
  480.         }
  481.         }
  482.     if (!tostdout)
  483.             if (chmod(workfilename, WORKMODE(RCSstat.st_mode))<0)
  484.                 warn("Can't adjust mode of %s",workfilename);
  485.  
  486.  
  487.         if (!tostdout) diagnose("done");
  488.         } while (cleanup(),
  489.                  ++argv, --argc >=1);
  490.  
  491.         exit(nerror!=0);
  492.  
  493. }       /* end of main (co) */
  494.  
  495.  
  496. /*****************************************************************
  497.  * The following routines are auxiliary routines
  498.  *****************************************************************/
  499.  
  500. int rmworkfile()
  501. /* Function: unlinks workfilename, if it exists, under the following conditions:
  502.  * If it is read-only, workfilename is unlinked.
  503.  * Otherwise (file writable):
  504.  *   if !quietmode asks the user whether to really delete it (default: fail);
  505.  *   otherwise failure.
  506.  * Returns false on failure to unlink, true otherwise.
  507.  */
  508. {
  509.         int response, c;    /* holds user response to queries */
  510.  
  511.         if (haveworkstat< 0)      /* File doesn't exist; set by pairfilenames*/
  512.             return (true);        /* No problem */
  513.  
  514. #ifdef MSDOS
  515.     if ((workstat.st_mode & S_IWRITE)&&!forceflag) {    /* File is writable */
  516. #else
  517.     if ((workstat.st_mode & 0222)&&!forceflag) {    /* File is writable */
  518. #endif /* MSDOS */
  519.             if (!quietflag) {
  520.                 VOID fprintf(stderr,"writable %s exists; overwrite? [ny](n): ",workfilename);
  521.                 /* must be stderr in case of IO redirect */
  522.                 c=response=getchar();
  523.                 while (!(c==EOF || c=='\n')) c=getchar(); /*skip rest*/
  524.                 if (!(response=='y'||response=='Y')) {
  525.                         warn("checkout aborted.");
  526.                         return false;
  527.                 }
  528.             } else {
  529.                 error("writable %s exists; checkout aborted.",workfilename);
  530.                 return false;
  531.             }
  532.         }
  533.     /* now unlink: either not writable, forceflag, or permission given */
  534.         if (unlink(workfilename) != 0) {            /* Remove failed   */
  535.             error("Can't unlink %s",workfilename);
  536.             return false;
  537.         }
  538.         return true;
  539. }
  540.  
  541.  
  542. creatempty()
  543. /* Function: creates an empty working file.
  544.  * First, removes an existing working file with rmworkfile().
  545.  */
  546. {
  547.         int  fdesc;              /* file descriptor */
  548.  
  549.         if (!rmworkfile()) return false;
  550.         fdesc=creat(workfilename,0);
  551.         if (fdesc < 0) {
  552.                 faterror("Cannot create %s",workfilename);
  553.                 return false;
  554.         } else {
  555.                 VOID close(fdesc); /* empty file */
  556.                 return true;
  557.         }
  558. }
  559.  
  560.  
  561. int rmlock(who,delta)
  562. char * who; struct hshentry * delta;
  563. /* Function: removes the lock held by who on delta.
  564.  * Returns -1 if someone else holds the lock,
  565.  * 0 if there is no lock on delta,
  566.  * and 1 if a lock was found and removed.
  567.  */
  568. {       register struct lock * next, * trail;
  569.         char * num;
  570.         struct lock dummy;
  571.         int whomatch, nummatch;
  572.  
  573.         num=delta->num;
  574.         dummy.nextlock=next=Locks;
  575.         trail = &dummy;
  576.         while (next!=nil) {
  577.                 whomatch=strcmp(who,next->login);
  578.                 nummatch=strcmp(num,next->delta->num);
  579.                 if ((whomatch==0) && (nummatch==0)) break;
  580.                      /*found a lock on delta by who*/
  581.                 if ((whomatch!=0)&&(nummatch==0)) {
  582.                     error("revision %s locked by %s; use co -r or rcs -u",num,next->login);
  583.                     return -1;
  584.                 }
  585.                 trail=next;
  586.                 next=next->nextlock;
  587.         }
  588.         if (next!=nil) {
  589.                 /*found one; delete it */
  590.                 trail->nextlock=next->nextlock;
  591.                 Locks=dummy.nextlock;
  592.                 next->delta->lockedby=nil; /* reset locked-by */
  593.                 return 1; /*success*/
  594.         } else  return 0; /*no lock on delta*/
  595. }
  596.  
  597.  
  598.  
  599.  
  600. /*****************************************************************
  601.  * The rest of the routines are for handling joins
  602.  *****************************************************************/
  603.  
  604. char * getrev(sp, tp, buffsize)
  605. register char * sp, *tp; int buffsize;
  606. /* Function: copies a symbolic revision number from sp to tp,
  607.  * appends a '\0', and returns a pointer to the character following
  608.  * the revision number; returns nil if the revision number is more than
  609.  * buffsize characters long.
  610.  * The revision number is terminated by space, tab, comma, colon,
  611.  * semicolon, newline, or '\0'.
  612.  * used for parsing the -j option.
  613.  */
  614. {
  615.         register char c;
  616.         register int length;
  617.  
  618.         length = 0;
  619.         while (((c= *sp)!=' ')&&(c!='\t')&&(c!='\n')&&(c!=':')&&(c!=',')
  620.                 &&(c!=';')&&(c!='\0')) {
  621.                 if (length>=buffsize) return false;
  622.                 *tp++= *sp++;
  623.                 length++;
  624.         }
  625.         *tp= '\0';
  626.         return sp;
  627. }
  628.  
  629.  
  630.  
  631. int preparejoin()
  632. /* Function: Parses a join list pointed to by join and places pointers to the
  633.  * revision numbers into joinlist.
  634.  */
  635. {
  636.         struct hshentry * * joindeltas;
  637.         struct hshentry * tmpdelta;
  638.         register char * j;
  639.         char symbolrev[revlength],numrev[revlength];
  640.  
  641.         joindeltas = (struct hshentry * *)talloc(hshsize*sizeof(struct hshentry *));
  642.         j=join;
  643.         lastjoin= -1;
  644.         for (;;) {
  645.                 while ((*j==' ')||(*j=='\t')||(*j==',')) j++;
  646.                 if (*j=='\0') break;
  647.                 if (lastjoin>=joinlength-2) {
  648.                         error("too many joins");
  649.                         return(false);
  650.                 }
  651.                 if(!(j=getrev(j,symbolrev,revlength))) return false;
  652.                 if (!expandsym(symbolrev,numrev)) return false;
  653.                 tmpdelta=genrevs(numrev,(char *)nil,(char *)nil,(char *)nil,(struct hshentry * *)joindeltas);
  654.                 if (tmpdelta==nil)
  655.                         return false;
  656.                 else    joinlist[++lastjoin]=tmpdelta->num;
  657.                 while ((*j==' ') || (*j=='\t')) j++;
  658.                 if (*j == ':') {
  659.                         j++;
  660.                         while((*j==' ') || (*j=='\t')) j++;
  661.                         if (*j!='\0') {
  662.                                 if(!(j=getrev(j,symbolrev,revlength))) return false;
  663.                                 if (!expandsym(symbolrev,numrev)) return false;
  664.                                 tmpdelta=genrevs(numrev,(char *)nil,(char *)nil,(char *)nil, (struct hshentry * *) joindeltas);
  665.                                 if (tmpdelta==nil)
  666.                                         return false;
  667.                                 else    joinlist[++lastjoin]=tmpdelta->num;
  668.                         } else {
  669.                                 error("join pair incomplete");
  670.                                 return false;
  671.                         }
  672.                 } else {
  673.                         if (lastjoin==0) { /* first pair */
  674.                                 /* common ancestor missing */
  675.                                 joinlist[1]=joinlist[0];
  676.                                 lastjoin=1;
  677.                                 /*derive common ancestor*/
  678.                                 joinlist[0]=talloc(revlength);
  679.                                 if (!getancestor(targetdelta->num,joinlist[1],joinlist[0]))
  680.                                        return false;
  681.                         } else {
  682.                                 error("join pair incomplete");
  683.                                 return false;
  684.                         }
  685.                 }
  686.         }
  687.         if (lastjoin<1) {
  688.                 error("empty join");
  689.                 return false;
  690.         } else  return true;
  691. }
  692.  
  693.  
  694.  
  695. buildjoin(initialfile)
  696. char * initialfile;
  697. /* Function: merge pairs of elements in joinlist into initialfile
  698.  * If tostdout==true, copy result to stdout.
  699.  * All unlinking of initialfile, rev2, and rev3 should be done by cleanup().
  700.  */
  701. {
  702.     char commarg[revlength+3];
  703.         char subs[revlength];
  704.         char * rev2, * rev3;
  705.         int i;
  706.  
  707. #ifdef MSDOS
  708.         rev2=mktempfile(tmpdir,JOINFIL2);
  709.         rev3=mktempfile(tmpdir,JOINFIL3);
  710. #else
  711.         rev2=mktempfile("/tmp/",JOINFIL2);
  712.         rev3=mktempfile("/tmp/",JOINFIL3);
  713. #endif /* MSDOS */
  714.  
  715.         i=0;
  716.         while (i<lastjoin) {
  717.                 /*prepare marker for merge*/
  718.                 if (i==0)
  719.                         VOID strcpy(subs,targetdelta->num);
  720.                 else    VOID sprintf(subs, "merge%d",i/2);
  721.                 diagnose("revision %s",joinlist[i]);
  722.                 VOID sprintf(commarg,"-p%s",joinlist[i]);
  723.                 if (run((char*)nil,rev2, co,commarg,"-q",RCSfilename,(char*)nil)) {
  724.                         nerror++;return false;
  725.                 }
  726.                 diagnose("revision %s",joinlist[i+1]);
  727.                 VOID sprintf(commarg,"-p%s",joinlist[i+1]);
  728.                 if (run((char *)nil,rev3, co,commarg,"-q",RCSfilename,(char*)nil)) {
  729.                         nerror++; return false;
  730.                 }
  731.                 diagnose("merging...");
  732.         if (
  733.                         (i+2)>=lastjoin && tostdout
  734.             ?    run((char*)nil,(char*)nil, merge,"-p",initialfile,rev2,rev3,subs,joinlist[i+1],(char*)nil)
  735.             :    run((char*)nil,(char*)nil, merge,     initialfile,rev2,rev3,subs,joinlist[i+1],(char*)nil)) {
  736.                         nerror++; return false;
  737.                 }
  738.                 i=i+2;
  739.         }
  740.         return true;
  741. }
  742.